package ca.ubc.cpsc210.waldo.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import org.junit.Test;

import ca.ubc.cpsc210.waldo.exceptions.IllegalBusStopException;
import ca.ubc.cpsc210.waldo.model.BusRoute;
import ca.ubc.cpsc210.waldo.model.BusStop;
import ca.ubc.cpsc210.waldo.translink.TranslinkService;
import ca.ubc.cpsc210.waldo.translink.TranslinkServiceFromJSONFile;
import ca.ubc.cpsc210.waldo.translink.TranslinkServiceFromXMLFile;
import ca.ubc.cpsc210.waldo.util.LatLon;

public class BusStopTests {

	// The service to test
	private TranslinkService service;

	// Files used in testing
	private final String TESTMIXEDFORMAT = "TestFiles/Student/UBCBusStops-TestMixedFormat";
	
	/**
	 * Test an XML file with some parts separated by new lines and others not.
	 * All input is well-formed
	 */
	@Test
	public void testReadInBusStopsFromWellFormedMixedFormat() {
		// Initialize the service
		service = new TranslinkServiceFromXMLFile();
		try {
			FileReader input = new FileReader(TESTMIXEDFORMAT + ".xml");
			// The file is relative to the location about to be set
			LatLon location = new LatLon(49.26636, -123.24158);
			service.getBusStopsAround(location, 500, input);
			Set<BusStop> stops = service.getBusStops();
			Set<BusStop> expectedStops = createStopsExpected(TESTMIXEDFORMAT
					+ ".txt");
			assertEquals(expectedStops.size(), stops.size());
			compareBusStopSets(expectedStops, stops);
		} catch (FileNotFoundException e) {
			fail("Unable to locate testing file");
		}

	}
	


	/**
	 * Compare two bus stop sets for equality
	 * 
	 * @param expectedStops
	 *            The expected stops
	 * @param stops
	 *            The parsed stops
	 */
	private void compareBusStopSets(Set<BusStop> expectedStops,
			Set<BusStop> stops) {

		// Loop over every expected stop
		for (BusStop s : expectedStops) {
			// Equals on bus stops only compares numbers. If the number is not
			// in the other set, fail
			if (stops.contains(s)) {
				// Find the matching bus stop that has been parsed and compare
				// all fields
				for (BusStop aStop : stops) {
					if (s.getNumber() == aStop.getNumber())
						compareAllFields(s, aStop);
				}
			} else
				fail("Missing the following bus stop: " + s.toString());
		}
	}

	/**
	 * Compare all fields of two bus stops
	 * 
	 * @param expectedStop
	 *            The stop with the correct fields
	 * @param aStop
	 *            The stop to compare with parsed fields
	 */
	private void compareAllFields(BusStop expectedStop, BusStop aStop) {
		// First match the straightforward fields of name and lat/lon
		assertTrue("Bus stop names do not match: " + expectedStop + " | "
				+ aStop, expectedStop.getName().equals(aStop.getName()));

		assertEquals("Bus stop lat/lon do not match: " + expectedStop + " | "
				+ aStop, expectedStop.getLatLon(), aStop.getLatLon());

		// Matching routes is a bit more complex
		assertEquals("Bus routes do not match: " + expectedStop + "| " + aStop,
				expectedStop.getRoutes().size(), aStop.getRoutes().size());
		Set<BusRoute> expectedRoutes = expectedStop.getRoutes();
		Set<BusRoute> aStopRoutes = aStop.getRoutes();
		for (BusRoute r : expectedRoutes) {
			// If the parsed routes does not contain an expected route, fail
			if (!aStopRoutes.contains(r))
				fail("Stop: <" + expectedStop.getNumber()
						+ "> is missing route " + r.toString());
		}
	}

	/**
	 * Reads in oracle data from a text file
	 * 
	 * @param filename
	 *            The name of the file to read from
	 */
	private Set<BusStop> createStopsExpected(String filename) {
		BufferedReader reader = null;
		Set<BusStop> expectedStops = new HashSet<BusStop>();
		try {
			reader = new BufferedReader(new FileReader(filename));
			String line = reader.readLine();
			while (line != null) {
				StringTokenizer st = new StringTokenizer(line, ":");
				while (st.countTokens() == 5) {
					int stopNumber = Integer.parseInt(st.nextToken());
					String stopName = st.nextToken().trim();
					
					double lat = Double.parseDouble(st.nextToken());
					double lon = Double.parseDouble(st.nextToken());
					String routesAsStrings = st.nextToken();
					StringTokenizer st1 = new StringTokenizer(routesAsStrings,
							",");
					List<String> routeNumbers = new ArrayList<String>();
					while (st1.hasMoreTokens()) {
						String trimmedString = st1.nextToken().trim();
						if (trimmedString.length() > 0)
							routeNumbers.add(trimmedString);
					}
					Set<BusRoute> routes = service
							.translateRoutes(routeNumbers);
					BusStop theStop = new BusStop(stopNumber, stopName,
							new LatLon(lat, lon), routes);
					expectedStops.add(theStop);
					line = reader.readLine();
				}
			}
		} catch (IOException e) {
			fail("Unable to open oracle file: <" + filename + ">");
		} finally {
			try {
				if (reader != null)
					reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return expectedStops;
	}

}
